import datetime
import math
import os
import pickle
import platform
import re
import subprocess
import sys
import time

import yaml
from appium.webdriver.common.touch_action import TouchAction
from selenium.common.exceptions import NoSuchElementException
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait

import config.configElement as eles
import utils.get_apk_info as apk_base
import utils.get_config as config
from utils.log import err_info
from utils.log import info_log
from utils.report import OperateFile
from selenium.webdriver.support.wait import WebDriverWait


def is_mac():
    """
    判断是不是 MAC 系统
    @return:
    """
    if platform.system() == 'Darwin':
        return True
    else:
        return False


def assert_btn(driver, location):
    r"""判断该节点是否存在"""
    time.sleep(2)
    loc_list = location.strip().split('=:=')
    assert wait_visible(driver, (loc_list[0], loc_list[1]))


def assert_toast(text):
    r"""获取toast内容，判断是否与预期一致"""
    # ######### 获取toast值 #################
    toast_text = text
    assert toast_text == text


def assert_text(driver, location, text):
    r"""判断节点的text内容是否正确"""
    element = get_element(driver, location)
    if element is not None:
        act_text = element.text
        if text in act_text:
            info_log.info('验证通过')
            assert True
        else:
            info_log.error('验证失败')
            assert False
    else:
        info_log.error('需验证的节点不存在')
        assert False


def slide(driver, bef_x1, bef_y1, aft_x1, aft_y1):
    r"""参数均是屏幕比例，从(bef_x1,bef_y1)移动到(aft_x1,aft_y1)"""
    screen_size = driver.get_window_size()
    bef_x = screen_size.get('width') * bef_x1
    aft_x = screen_size.get('width') * aft_x1
    bef_y = screen_size.get('height') * bef_y1
    aft_y = screen_size.get('height') * aft_y1
    driver.swipe(bef_x, bef_y, aft_x, aft_y, 500)


def wait_ele_slide(driver, location):
    i = 0
    loc_list = location.strip().split('=:=')
    if loc_list[0] == 'name':
        by = By.NAME
    elif loc_list[0] == 'id':
        by = By.ID
    else:
        by = By.XPATH
    while not wait_visible(driver, (by, loc_list[1]), False) and i < 10:
        slide(driver, 0.5, 0.8, 0.5, 0.2)
        i = i + 1
        time.sleep(60)


def page_turn(driver):
    i = 0
    while i < 3:
        slide(driver, 0.5, 0.8, 0.5, 0.2)
        i = i + 1
    while i < 6:
        slide(driver, 0.5, 0.2, 0.5, 0.8)
        i = i + 1


def is_visible_dri(driver, by, value, is_log=True):
    try:
        driver.find_element(by, value)
        return True
    except Exception as e:
        if is_log:
            err_info.error('节点不存在：{}, {}'.format((by, value), e))
        return False


def is_visible_ele(element, by, value, is_log=True):
    try:
        element.find_element(by, value)
        return True
    except Exception as e:
        if is_log:
            err_info.error('节点不存在：{}, {}'.format((by, value), e))
        return False


def wait_clickable(driver, locator, is_log=True):
    try:
        WebDriverWait(driver, 10).until(EC.element_to_be_clickable(locator))
        return True
    except Exception as e:
        if is_log:
            err_info.error('节点不可点击:{} ,{}'.format(locator, e))
        return False


def wait_visible(driver, locator, is_log=True):
    try:
        WebDriverWait(driver, 10).until(EC.visibility_of_element_located(locator))
        return True
    except Exception as e:
        if is_log:
            err_info.error('节点不存在:{}, {}'.format(locator, e))
        return False


def date_to_string(date=datetime.datetime.now()):
    return date.strftime('%Y-%m-%d')


def get_time():
    tamp = int(time.time())
    return tamp


def call_adb(command):
    command_result = ''
    command_text = 'adb %s' % command
    results = os.popen(command_text, "r")
    while 1:
        line = results.readline()
        if not line:
            break
        command_result += line
    results.close()
    return command_result


def get_online_devices():
    """
    获取所有在线设备
    :return:
    """
    result = call_adb("devices")
    devices = result.partition('\n')[2].replace('\n', '').split('\tdevice')
    return_devices = []
    for device in devices:
        if len(device) > 0:
            return_devices.append(device)
    return return_devices


def _get_screen_size(device_name):
    """
    获取手机屏幕大小
    :return:
    """
    result = call_adb(" -s " + device_name + " shell wm size ")
    if not result:
        print('请安装 ADB 及驱动并配置环境变量')
        sys.exit()
    m = re.search(r'(\d+)x(\d+)', result)
    return "{width}x{height}".format(width=m.group(1), height=m.group(2))


def input_paypwd(driver, device_name, pay_pwd):
    """
    输入支付密码
    :param device_name:
    :param driver:
    :param pay_pwd:
    :return:
    """
    keyboard = {
        "1": [300, 1200],
        "2": [540, 1200],
        "3": [780, 1200],
        "4": [300, 1370],
        "5": [540, 1370],
        "6": [780, 1370],
        "7": [300, 1540],
        "8": [540, 1540],
        "9": [780, 1540],
        "0": [540, 1700],
    }

    for i in pay_pwd:
        x = keyboard[i][0]
        y = keyboard[i][1]
        time.sleep(1)
        tap(driver, device_name, x, y)


def operate_has_element(driver, mOperate):
    return has_element(driver, mOperate['element_info'])



def get_yam(home_yaml):
    try:
        with open(home_yaml, encoding='utf-8') as f:
            return yaml.full_load(f)
    except FileNotFoundError:
        print(u"找不到文件")


def get_phone_info(devices):
    cmd = "adb -s " + devices + " shell cat /system/build.prop "
    # phone_info = os.popen(cmd, mode="r").readlines()
    phone_info = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).stdout.readlines()

    l_list = {}
    release = "ro.build.version.release="  # 版本
    model = "ro.product.model="  # 型号
    brand = "ro.product.brand="  # 品牌
    device = "ro.product.device="  # 设备名
    for line in phone_info:
        for i in line.split():
            temp = i.decode()
            if temp.find(release) >= 0:
                l_list["release"] = temp[len(release):]
                break
            if temp.find(model) >= 0:
                l_list["model"] = temp[len(model):]
                break
            if temp.find(brand) >= 0:
                l_list["brand"] = temp[len(brand):]
                break
            if temp.find(device) >= 0:
                l_list["device"] = temp[len(device):]
                break
    info_log.info('设备信息:{}'.format(l_list))
    return l_list


# 得到最大运行内存
def get_men_total(devices):
    cmd = "adb -s " + devices + " shell cat /proc/meminfo"
    get_cmd = os.popen(cmd).readlines()
    men_total = 0
    men_total_str = "MemTotal"
    for line in get_cmd:
        if line.find(men_total_str) >= 0:
            men_total = line[len(men_total_str) + 1:].replace("kB", "").strip()
            break
    return int(men_total)


# 得到几核cpu
def get_cpu_kel(devices):
    cmd = "adb -s " + devices + " shell cat /proc/cpuinfo"
    get_cmd = os.popen(cmd).readlines()
    find_str = "processor"
    int_cpu = 0
    for line in get_cmd:
        if line.find(find_str) >= 0:
            int_cpu += 1
    return str(int_cpu) + "核"


# 得到手机分辨率
def get_app_pix(devices):
    result = os.popen("adb -s " + devices + " shell wm size", "r")
    return result.readline().split("Physical size:")[1]


def get_avg_raw(l_men, devices):
    '''

    :param l_men: 内存使用列表
    :param devices: 设备名
    :return:
    '''
    l_men = [math.ceil(((l_men[i]) / get_men_total(devices)) * 1024) for i in range(len(l_men))]  # 获取每次占用内存多少
    if len(l_men) > 0:
        return str(math.ceil(sum(l_men) / len(l_men))) + "%"
    return "0%"


def phone_avg_use_cpu(cpu):
    result = ""
    try:
        if len(cpu) > 0:
            result = str(math.ceil(sum(cpu) / len(cpu))) + "%"
        return result
    except:
        return result


def phone_avg_use_raw(men):
    try:
        if len(men) > 0:
            return str(math.ceil(sum(men) / len(men))) + "%"
        return 0
    except:
        return 0


def phone_max_use_raw(l_men):
    if len(l_men) > 0:
        return str(math.ceil((max(l_men)) / 1024)) + "M"
    return "0"


def phone_avg_max_use_cpu(cpu):
    try:
        if len(cpu):
            return str(max(cpu)) + "%"
        return "0"
    except:
        return "0"


def phone_raw(raw):
    if raw > 0:
        return str(math.ceil(raw)) + "M"
    return "0"


def fps_max(d_fps):
    if len(d_fps) > 0:
        return str(max(d_fps))
    return "0"


def fps_avg(d_fps):
    result = 0
    if len(d_fps) > 0:
        result = float(str(math.ceil(sum(d_fps) / len(d_fps))))
        return '%.2f' % result
    return result


def top_cpu(devices, pkg_name):
    cmd = "adb -s " + devices + " shell dumpsys cpuinfo | grep -w " + pkg_name + ":"
    get_cmd = os.popen(cmd).readlines()
    for info in get_cmd:
        return float(info.split()[2].split("%")[0])


# 得到men的使用情况
def get_men(devices, pkg_name):
    cmd = "adb -s " + devices + " shell  dumpsys  meminfo %s" % (pkg_name)
    total = "TOTAL"
    get_cmd = os.popen(cmd).readlines()
    for info in get_cmd:
        info_sp = info.strip().split()
        for item in range(len(info_sp)):
            if info_sp[item] == total:
                return int(info_sp[item + 1])
    return 0


# 得到fps
def get_fps(devices, pkg_name):
    print("fps-")
    _adb = "adb -s " + devices + " shell dumpsys gfxinfo %s | grep -A 128 'Execute'  | grep -v '[a-Z]' " % pkg_name
    result = os.popen(_adb).read().strip()
    result = result.split('\r\n')
    for i in result:
        l_result = i.split('\t')[-3:]
        f_sum = 0
        for j in l_result:
            r = re.search(r"\d+\.\d+", str(j))
            if r:
                f_sum += float(r.group())
        return float('%.2f' % f_sum)


def write_pickle(dict_data, path="data.pickle"):
    read = read_pickle(path)
    result = []
    if len(read) > 0:
        read.append(dict_data)
        result = read
    else:
        result.append(dict_data)
    with open(path, 'wb') as f:
        print(result)
        pickle.dump(result, f, 0)


def read_pickle(path):
    data = {}
    if OperateFile(path).check_file():
        with open(path, 'rb') as f:
            try:
                data = pickle.load(f)
            except EOFError:
                pass
    print(data)
    return data


def apk_pkg_info():
    return apk_base.ApkInfo(config.configs["app_path_android"]).get_apk_pkg()


def has_element(driver, location):
    """
    判断元素是否存在
    :param driver:
    :param location:
    :return:
    """
    element = get_element(driver, location)
    if element is not None:
        return True
    else:
        return False


def send_key(driver, location, value):
    r"""给节点输入值
    :param
    driver: 驱动
    location: 获取节点的方式，如: id='thisIsId',name='thisIsName',xpath='thisIsXpath' """
    element = get_element(driver, location, False)
    if element is not None:
        element.clear()
        element.send_keys(value)
    else:
        raise NoSuchElementException


def click_btn(driver, location):
    r"""点击节点
    :param
    driver: 驱动
    location: 获取节点的方式，如: id=:='thisIsId',name=:='thisIsName',xpath=:='thisIsXpath' """
    element = get_element(driver, location, False)
    if element is not None:
        element.click()
    else:
        raise NoSuchElementException


def back(driver):
    """
    向后回退一次
    @param driver::
    @return:
    """
    time.sleep(1)
    # if has_element(driver, "common.btn_back"):
    #     click_btn(driver, "common.btn_back")
    # else:
    #     driver.back()
    driver.back()


def sleep(driver, sec):
    time.sleep(sec)


# 获取元素上的文字
def get_element_text(driver, location):
    element = get_element(driver, location, False)
    if element is not None:
        return element.text
    else:
        raise NoSuchElementException


def get_element(driver, location, is_wait=True, is_log=True):
    """
    获取元素
    @param driver:
    @param location:
    @param is_wait:
    @param is_log:
    @return:
    """
    element = None
    loc_list = str(location).strip().split('.')
    ele_str = eles.elements[loc_list[0]][loc_list[1]]

    if ele_str['type'].lower() == 'id' and len(loc_list) > 1:
        ele_key = config.configs['prefix'] + ':id/' + ele_str['key']
        if is_wait:
            if wait_visible(driver, (By.ID, ele_key), is_log):
                element = driver.find_element_by_id(ele_key)
        else:
            element = driver.find_element_by_id(ele_key)
    elif ele_str['type'].lower() == 'name' and len(loc_list) > 1:
        if is_wait:
            if wait_visible(driver, (By.NAME, ele_str['key']), is_log):
                element = driver.find_element_by_name(ele_str['key'])
        else:
            element = driver.find_element_by_name(ele_str['key'])
    elif ele_str['type'].lower() == 'xpath' and len(loc_list) > 1:
        if is_wait:
            if wait_visible(driver, (By.XPATH, ele_str['key']), is_log):
                element = driver.find_element_by_xpath(ele_str['key'])
        else:
            element = driver.find_element_by_xpath(ele_str['key'])
    elif ele_str['type'].lower() == 'aid' and len(loc_list) > 1:
        ele_key = ele_str['key']
        if is_wait:
            if wait_visible(driver, (By.ID, ele_key), is_log):
                element = driver.find_element_by_id(ele_key)
        else:
            element = driver.find_element_by_id(ele_key)
    else:
        err_info.error('节点元素配置不正确：{}'.format(location))
    return element


def load_ele(driver, mOperate):
    """
    加载元素
    @param driver:
    @param mOperate:
    @return:
    """
    while has_element(driver, mOperate):
        time.sleep(1)


def back_to_ele(driver, mOperate):
    """
        回退元素
        @param driver:
        @param mOperate:
        @return:
        """
    time.sleep(1)
    i = 0
    while not has_element(driver, mOperate):
        back(driver)
        i = i + 1
        if i == 6:
            break


def _get_screen_ratio(x, y):
    """
    获取手机屏幕比例
    :param x:
    :param y:
    :return:
    """
    relx = x / 1080
    rely = y / 1920
    return [relx, rely]


def tap(driver, device_name, x, y):
    """
    点击屏幕
    :param device_name:
    :param driver:
    :param x:
    :param y:
    :return:
    """
    x = int(_get_screen_size(device_name)[0:4]) * _get_screen_ratio(x, y)[0]
    y = int(_get_screen_size(device_name)[5:]) * _get_screen_ratio(x, y)[1]
    TouchAction(driver).tap(x=int(x), y=int(y)).perform()


def press_move(driver, device_name, element, press_x, press_y, move_x, move_y):
    """
    按住屏幕上的一个点拖动到屏幕上的另一个点
    :param driver:
    :param device_name: 设备名称
    :param element:要滑动到的元素
    :param press_x 按住的点的x坐标
    :param press_y 按住的点的y坐标
    :param move_x 目标点的x坐标
    :param move_y 目标点的x坐标
    :return:
    """
    press_x = int(_get_screen_size(device_name)[0:4]) * _get_screen_ratio(press_x, press_y)[0]
    press_y = int(_get_screen_size(device_name)[5:]) * _get_screen_ratio(press_x, press_y)[1]
    move_x = int(_get_screen_size(device_name)[0:4]) * _get_screen_ratio(move_x, move_y)[0]
    move_y = int(_get_screen_size(device_name)[5:]) * _get_screen_ratio(move_x, move_y)[1]
    TouchAction(driver).press(x=int(press_x), y=int(press_y)).move_to(x=int(move_x),
                                                                      y=int(move_y)).release().perform()

    for x in range(5):
        if has_element(driver, element):
            break
        else:
            TouchAction(driver).press(x=int(press_x), y=int(press_y / 2)).move_to(x=int(move_x),
                                                                                  y=int(move_y / 2)).release().perform()

def screen_shot(driver, case_no):
    r"""截图"""
    screen_shot = ROOT_PATH + config.configs['screenShot_path']
    file_name = screen_shot + case_no + '_' + str(get_time()) + '.png'
    try:
        driver.get_screenshot_as_file(file_name)
    except Exception as e:
        file_name = None
        err_info.error('截图失败!{}'.format(e))
    return file_name


def operate_element(driver, serial, mOperate):
    """
    操作元素对象
    :param driver: 驱动
    :param serial: 设备信息对象
    :param mOperate: 操作元素对象
    :return:
    """
    try:
        elements = {
            CLICK: lambda: click_btn(driver, mOperate['element_info']),
            CAMERA_SHOT: lambda: click_btn(driver, serial['camera_check']),
            CAMERA_DONE: lambda: click_btn(driver, serial['camera_done']),
            SEND_KEYS: lambda: send_key(driver, mOperate['element_info'], mOperate['text']),
            SEND_SUBSCRIBER: lambda: send_key(driver, mOperate['element_info'], serial['subscriber']),
            SEND_EMAIL: lambda: send_key(driver, mOperate['element_info'], serial['email']),
            SEND_PASSWORD: lambda: send_key(driver, mOperate['element_info'], serial['password']),
            GET_ELEMENT_TEXT: lambda: get_element_text(driver, mOperate['element_info']),
            PRESS_MOVE: lambda: press_move(driver, serial['name'], mOperate['element_info'], mOperate['press_x'],
                                           mOperate['press_y'], mOperate['move_x'], mOperate['move_y']),
            SLEEP: lambda: sleep(driver, mOperate['sec']),
            BACK: lambda: back(driver),
            BACK_TO_ELE: lambda: back_to_ele(driver,mOperate['element_info']),
            SCREEN_SHOT: lambda: screen_shot(driver, mOperate['case_no']),
            LOAD_ELE: lambda: load_ele(driver, mOperate['element_info']),
        }
        if mOperate['operate_type'] == "press_move":
            return elements[mOperate["operate_type"]]()
        if mOperate['operate_type'] == "back":
            return elements[mOperate["operate_type"]]()
        if mOperate['operate_type'] == "back_to_ele":
            return elements[mOperate["operate_type"]]()
        if mOperate['operate_type'] == "sleep":
            return elements[mOperate["operate_type"]]()
        if mOperate['operate_type'] == "camera_shot":
            return elements[mOperate["operate_type"]]()
        if mOperate['operate_type'] == "camera_done":
            return elements[mOperate["operate_type"]]()
        if has_element(driver, mOperate['element_info']):
            return elements[mOperate["operate_type"]]()
    except Exception as e:
        return False
    return False


CLICK = "click"  # 点击元素
SEND_CODE = "send_code"  # 输入支付密码
SEND_KEYS = "send_keys"  # 输入内容
SEND_SUBSCRIBER= "send_subscriber_keys"  # 输入承包商
SEND_EMAIL = "send_email_keys"  # 输入邮箱账户名
SEND_PASSWORD = "send_password_keys"  # 输入登录密码
PRESS_MOVE = "press_move"  # 滑动页面
GET_ELEMENT_TEXT = "get_element_text"  # 获取文字
SLEEP = "sleep" # 设备休眠
BACK = "back"  # 页面回退
BACK_TO_ELE = "back_to_ele" # 回退到指定元素
SCREEN_SHOT = "screen_shot"  # 截图
LOAD_ELE = "load_ele"  # 加载元素
CAMERA_SHOT = "camera_shot" # 相机拍摄按钮
CAMERA_DONE = "camera_done" # 拍摄确认按钮

# 项目根目录
ROOT_PATH = config.configs['project_path']
REPORT_PATH = ROOT_PATH + config.configs['report_path']
# 本地存储记录所有的case情况的路径
REPORT_INIT = REPORT_PATH + config.configs['report_init_name']
CRASH_LOG_PATH = REPORT_PATH + config.configs['report_crash_name']
REPORT_INFO_PATH = REPORT_PATH + config.configs['report_info_name']
REPORT_COLLECT_PATH = REPORT_PATH + config.configs['report_collect_name']

if __name__ == '__main__':
    pass
